package com.pijunn.payment

import javax.xml.datatype.DatatypeConfigurationException
import javax.xml.datatype.DatatypeFactory

import com.fedex.rate.Address
import com.fedex.rate.ClientDetail
import com.fedex.rate.Dimensions
import com.fedex.rate.DropoffType
import com.fedex.rate.LinearUnits
import com.fedex.rate.Money
import com.fedex.rate.NotificationSeverityType;
import com.fedex.rate.PackageSpecialServicesRequested
import com.fedex.rate.PackagingType
import com.fedex.rate.Party
import com.fedex.rate.Payment
import com.fedex.rate.PaymentType
import com.fedex.rate.RatePortType;
import com.fedex.rate.RateReplyDetail;
import com.fedex.rate.RateRequest
import com.fedex.rate.RateRequestType
import com.fedex.rate.RateService;
import com.fedex.rate.RequestedPackageLineItem
import com.fedex.rate.RequestedShipment
import com.fedex.rate.TransactionDetail
import com.fedex.rate.VersionId
import com.fedex.rate.WebAuthenticationCredential
import com.fedex.rate.WebAuthenticationDetail
import com.fedex.rate.Weight
import com.fedex.rate.WeightUnits
import com.pijunn.common.MailingAddress
import com.pijunn.common.enums.ServiceType
import com.pijunn.shipping.ShippingRate
import com.pijunn.webservice.fedex.RateWebServiceClient

class PaymentService {
	static transactional = false

	def grailsApplication
	
	RatePortType rateClient
	
	private BigDecimal handlingCharge = 25.00
	
	def getHandlingCharge() {
		handlingCharge
	}
	
	List<RateReplyDetail> getShippingCharge(MailingAddress from, MailingAddress to, ShippingRate rate, ServiceType type, BigDecimal insuranceAmount) {
		def clientDetail = createClientDetail()
		def webAuthenticationDetail = createWebAuthenticationDetail()
		
		// Build a RateRequest request object
		boolean getAllRatesFlag = false // set to true to get the rates for different service types
		RateRequest rateReq = new RateRequest(clientDetail: clientDetail, webAuthenticationDetail: webAuthenticationDetail, returnTransitAndCommit: true)
		//
		TransactionDetail transactionDetail = new TransactionDetail(customerTransactionId:"Rate Request for item return")  // The client will get the same value back in the response
		rateReq.transactionDetail = transactionDetail

		//
		VersionId versionId = new VersionId(serviceId:"crs", major: 13, intermediate:0, minor:0)
		rateReq.version = versionId
		
		//
		GregorianCalendar c = new GregorianCalendar()
		c.setTime(new Date())
		RequestedShipment requestedShipment = new RequestedShipment(dropoffType: DropoffType.STATION)
		try {
			requestedShipment.setShipTimestamp(DatatypeFactory.newInstance().newXMLGregorianCalendar(c))
		} catch (DatatypeConfigurationException e1) {
			// This should not happen as long as we use the current time
			e1.printStackTrace()
		}
		if (! getAllRatesFlag) {
			requestedShipment.serviceType = com.fedex.rate.ServiceType.valueOf(type.name())
			requestedShipment.packagingType = PackagingType.YOUR_PACKAGING
		}
		
		
		Party shipper = new Party(address: new Address(streetLines: [from.address1, from.address2], city: from.city, stateOrProvinceCode: from.state, postalCode: from.zipCode, countryCode: from.country))
		requestedShipment.shipper = shipper

		//
		Party recipient = new Party(address: new Address(streetLines: [to.address1, to.address2], city: to.city, stateOrProvinceCode: to.state, postalCode: to.zipCode, countryCode: to.country))
		requestedShipment.recipient = recipient

		//
		Payment shippingChargesPayment = new Payment(paymentType: PaymentType.SENDER)
		requestedShipment.shippingChargesPayment = shippingChargesPayment

		RequestedPackageLineItem rp = new RequestedPackageLineItem(groupPackageCount: new BigInteger("1"), weight: new Weight(units: WeightUnits.LB, value: new BigDecimal(rate.getEmptyWeight())), insuredValue: new Money(currency: "USD", amount: insuranceAmount))
		//
		def dim = new com.fedex.rate.Dimensions(height: new BigInteger(""+rate.getDimensions().getHeight()), length: new BigInteger(""+rate.getDimensions().getDepth()), width: new BigInteger(""+rate.getDimensions().getWidth()), units: LinearUnits.IN)
		rp.dimensions = dim
		rp.specialServicesRequested = new PackageSpecialServicesRequested()
		requestedShipment.requestedPackageLineItems = [rp]

		
		requestedShipment.packageCount = new BigInteger("1")
		requestedShipment.rateRequestTypes = [RateRequestType.LIST]
	
		rateReq.requestedShipment = requestedShipment

		
		def result = rateClient.getRates(rateReq)
		
		if (result.highestSeverity != NotificationSeverityType.SUCCESS) {
			for (notification in result.notifications) {
				log.error('FedEx Web Service Failure: ' + notification.message + ' Severity: ' + notification.severity)
			}
			
			throw new Exception('FedEx Web Service Failed!  See Log for details. Severity is: ' + result.highestSeverity)
		}
		
		return result.getRateReplyDetails()
	}
	
	def getShippingSpeeds() {
		return ServiceType.values()
	}
	
	def getShippingCarriers() {
		return ['FedEx']
	}
	
	def getShippingContainerTypes() {
		
		ShippingRate.list()
//		def criteria = ShippingRate.createCriteria()
//		return criteria {
//			projections {
//				distinct('packageType')
//			}
//		}
	}
	
	private ClientDetail createClientDetail() {
		ClientDetail clientDetail = new ClientDetail(
			accountNumber: grailsApplication.config.service.fedex.RateWebServiceClient.accountNumber,
			meterNumber: grailsApplication.config.service.fedex.RateWebServiceClient.meterNumber
			);
	}
	
	private WebAuthenticationDetail createWebAuthenticationDetail() {
		WebAuthenticationCredential wac = new WebAuthenticationCredential();
		def key = grailsApplication.config.service.fedex.RateWebServiceClient.key
		def password = grailsApplication.config.service.fedex.RateWebServiceClient.password
		
		wac.setKey(key);
		wac.setPassword(password);
		return new WebAuthenticationDetail(userCredential: wac);
	}

}
